﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;

using Eco.ObjectRepresentation;
using Eco.Services;
using Descriptions_DomainModel.Model;
using CommonClasses.Services;
using LPMEDTranslator.Model;

namespace CommandGraph.Model
{
  /// <summary>
  /// По заданному тексту и описанию оборудования предоставляет список 
  /// воможных дополнений до полных имён
  /// </summary>
  public class IHIntelliSenseProvider
  {
    private mutable serviceProvider : IEcoServiceProvider = null;
    public this(serviceProvider : IEcoServiceProvider)
    {
      this.serviceProvider = serviceProvider;
    }
    
    public GetCompleteList(text : string, 
        cursorLocation : Point, 
        variables : IList[string], 
        domainModel : DomainModel) : IEnumerable[string]
    {
      def CurrentContext()
      {
        def parser = cLPMEDIntelliSense(serviceProvider, cursorLocation);
        def translator = serviceProvider.GetEcoService.[ITranslatorService]();
        mutable tree = null;
        _ = translator.SyntaxCheck(text,
          parser,
          List(),
          out tree);
        ((parser : cLPMEDIntelliSense).ContextType, (parser : cLPMEDIntelliSense).Domains)
      }
      def AllAvailableNamesIn(context)
      {
        def (contType : string, domains : IList[string]) = context;
        
        def NodeWithFullName(fullName)
        {
          def NodeByFullName(fullName, source)
          {
            def node = source.Find(fun (node){node.Name == fullName.Head});
            match(node)
            {
              | Some => 
                {
                  match(fullName)
                  {
                    | lastName :: [] => node.Value
                    | _ => NodeByFullName(fullName.Tail, node.Value.Nodes)
                  }
                }
              | None => null
            }
          }
          
          def group = domainModel.NodeGroups
            .Find(fun (group){group.Name == fullName.Head});
            
          match(group)
          {
            | Some => NodeByFullName(fullName.Tail, group.Value.Nodes)
            | None => null
          }
        }
        def AllSuchTopLevelID(beginning)
        {
          variables.Filter(_.StartsWith(beginning))
                     .Append(domainModel.NodeGroups
                       .Filter(fun (group){group.Name.StartsWith(beginning)})
                       .Fold([], fun(group, acc){group.Name :: acc}))
                     .Append(domainModel.FunctionLibrary.Functions
                       .Filter(fun (func){func.Name.StartsWith(beginning)})
                       .Fold([], fun(group, acc){group.Name :: acc}))
        }
        def AllNodeMembersWithBeginning(node, beginning)
        {
          node.Nodes.Filter(fun(nod){nod.Name.StartsWith(beginning)})
            .Fold([], fun(nod, acc){nod.Name :: acc})
            .Append(node.NamedStates
              .Filter(fun(state){state.Name.StartsWith(beginning)})
              .Fold([], fun(state, acc){state.Name :: acc}))
            .Append(node.Methods
              .Filter(fun(meth){meth.Name.StartsWith(beginning)})
              .Fold([], fun(meth, acc){meth.Name :: acc}))
        }
        def AllGroupMembersWithBeginning(group, beginning)
        {
          group.Nodes.Filter(fun(node){node.Name.StartsWith(beginning)})
            .Fold([], fun(node, acc) {node.Name :: acc})
        }
        
        match(contType)
        {
          | "NewID" =>
            {
              match(domains.Count)
              {
                | 0 => []
                | 1 => AllSuchTopLevelID(domains[0]) //начали набирать имя группы
                | 2 => //набрали группу и начали набирать имя первого узла
                  {
                    def group = domainModel.NodeGroups.Find(fun (group)
                      {group.Name == domains[0]});
                    match(group)
                    {
                      | None => []
                      | Some => AllGroupMembersWithBeginning(group.Value, domains[1])
                    }
                  }
                | anyValue when anyValue > 2 => //общий случай
                  {
                    def (domainsCopy, _) = domains.Fold((List(), 0), fun(name, (acc, i)) 
                      {when (i < domains.Count - 1)
                         acc.Add(name);
                       (acc, i+1)});
                    def node = NodeWithFullName(domainsCopy.ToList());
                    match(node)
                    {
                      | null => []
                      | _ => AllNodeMembersWithBeginning(node, domains[domains.Count - 1]);
                    }
                  }
              }
            }
          | "AllChildren" =>
            {
              match(domains.Count)
              {
                | 0 => []
                | 1 => //набрали группу, поставили точку
                  {
                    def group = domainModel.NodeGroups.Find(fun (group)
                      {group.Name == domains[0]});
                    match(group)
                    {
                      | None => []
                      | Some => AllGroupMembersWithBeginning(group.Value, string.Empty)
                    }
                  }
                | anyValue when anyValue > 1 => 
                //общий случай - набрали N идентификаторов через точку, поставили ещё одну
                  {
                    def node = NodeWithFullName(domains.ToList());
                    match(node)
                    {
                      | null => []
                      | _ => "State" :: AllNodeMembersWithBeginning(node, string.Empty);
                    }
                  }
              }
            }
          | "Clear" => AllSuchTopLevelID(string.Empty)
          | _ => ["ups!"]
        }
      }
      
      AllAvailableNamesIn(CurrentContext())
        .Append(if (text != string.Empty) ["go()", "wait()"] else [])
        .Sort(CompareString);
    }
    
    private CompareString(a : string, b : string) : int
    {
      def compareChars(a, b)
      {
        | _ when a > b => 1
        | _ when a < b => -1
        | _ => 0
      }
      def compare(a : list[char], b : list[char])
      {
          | ([], []) => 0
          | ([], _) => -1
          | (_, []) => -1
          | _ =>
            match(compareChars(a.Head, b.Head))
            {
              | 0 => compare(a.Tail, b.Tail)
              | differ => differ
            }
      }
      
      compare(a.ToList(), b.ToList())
    }
  }
}